package juice;

import java.util.Properties;

import juice.DI$constant.ConstantInject;
import juice.DI$constructor.ConstructorInject;
import juice.DI$field.FieldInjectInstall;
import juice.DI$field.FieldMain;
import juice.DI$field.FieldMainStatic;
import juice.DI$multiInterface.service.Service;
import juice.DI$provider.annotation.ProviderService$Annotation;
import juice.DI$provider.module.HomeServiceProvider$Module;
import juice.DI$provider.module.ProviderService$Module;
import juice.DI$provider.provider.ProviderInject;
import juice.DI$scope.ThreadServiceScope;
import juice.DI$setter.SetterInject;
import juice.DI$typeOfmodule.HelloGuice;
import juice.DI$typeOfmodule.HelloGuiceModule;
import junit.Order;
import junit.OrderedRunner;
import junit.framework.TestCase;

import org.junit.Test;
import org.junit.runner.RunWith;

import com.google.inject.Binder;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.name.Names;

/*
 * 尽可能覆盖juice所有包的一个测试文件：不包含特殊的包,juice.DI$multiInterface.
 */
@RunWith(OrderedRunner.class)
public class TestHelloGuice extends TestCase
{
	/**
	 * 使用com.google.inject.Module接口实现类
	 * 
	 * 文件名说明 :<br>
	 * HelloGuice.java<br>
	 * 业务逻辑接口定义文件 HelloGuiceImpl.java<br>
	 * 业务逻辑接口实现文件 HelloGuiceModule.java,该文件必须实现com.google.inject.Module接口<br>
	 * 
	 */
	@Test
	@Order(order = 1)
	public void testHelloGuice()
	{
		System.out.println("=====================1");
		Injector injector = Guice.createInjector(new HelloGuiceModule());
		HelloGuice helloGuice = injector.getInstance(HelloGuice.class);
		helloGuice.sayHello();
	}

	/**
	 * 使用@ImplementedBy注解
	 * 
	 * 我们也可以直接为HelloGuice加上@ImplementedBy注解，而省略掉对com.google.inject.Module的实现。<br>
	 * HelloGuiceModule.java不再需要。其余的文件内容不变。
	 */
	@Test
	@Order(order = 2)
	public void testHelloGuiceOfAnnotation()
	{
		System.out.println("=====================2");
		Injector injector = Guice.createInjector();
		juice.DI$typeOfannotation.HelloGuice guice = injector.getInstance(juice.DI$typeOfannotation.HelloGuice.class);
		guice.sayHello();
	}

	/**
	 * 使用字段：非静态注入
	 */
	@Test
	@Order(order = 3)
	public void testField()
	{
		System.out.println("=====================3");
		FieldMain main = Guice.createInjector().getInstance(FieldMain.class);
		main.getService().execute();
	}

	/**
	 * 使用字段：静态注入
	 */
	@Test
	@Order(order = 4)
	public void testFieldStatic()
	{
		System.out.println("=====================4");
		// 可以与非静态注入一起在module中使用
		FieldMainStatic main = Guice.createInjector().getInstance(FieldMainStatic.class);
		main.getService().execute();

		// 静态注入
		Guice.createInjector(new FieldMainStatic());
		FieldMainStatic.service2.execute();
	}

	/**
	 * 构造函数注入
	 */
	@Test
	@Order(order = 5)
	public void testConstructor()
	{
		System.out.println("=====================5");
		ConstructorInject injector = Guice.createInjector().getInstance(ConstructorInject.class);
		injector.getService().execute();
	}

	/**
	 * setter注入
	 */
	@Test
	@Order(order = 6)
	public void testSetter()
	{
		System.out.println("=====================6");
		SetterInject injector = Guice.createInjector().getInstance(SetterInject.class);
		injector.getService().execute();
	}

	/**
	 * 注入实例变量的属性
	 */
	@Test
	@Order(order = 7)
	public void testFieldInjectInstall()
	{
		System.out.println("=====================7");
		/**
		 * Thinking In Java里面的说法（唯一正确的说法）： <br>
		 * 如果定义一个匿名内部类，并且希望它使用一个在其外部定的对象，那么编译器会要求其参数引用是final的。
		 */
		final FieldInjectInstall fii = new FieldInjectInstall();
		Guice.createInjector(new Module()
		{

			@Override
			public void configure(Binder arg0)
			{
				arg0.requestInjection(fii);
			}

		});
		fii.service.execute();
	}

	/**
	 * 实际上这里有一种简便的方法来注入字段，实际上此方法也支持Setter注入。
	 */
	@Test
	@Order(order = 8)
	public void testFieldInjectInstall2()
	{
		System.out.println("=====================8");
		FieldInjectInstall fii = new FieldInjectInstall();
		// 相对于构造函数注入人性化,在需要的时候实现手动注入。
		Guice.createInjector().injectMembers(fii);
		fii.service.execute();
	}

	/**
	 * provider:使用module
	 */
	@Test
	@Order(order = 9)
	public void testProvider$Module()
	{
		System.out.println("=====================9");
		Injector injector = Guice.createInjector(new Module()
		{
			@Override
			public void configure(Binder binder)
			{
				binder.bind(Service.class).toProvider(HomeServiceProvider$Module.class);
			}
		});

		ProviderService$Module service = injector.getInstance(ProviderService$Module.class);
		service.service.execute();
	}

	/**
	 * provider:使用@ProviderBy
	 */
	@Test
	@Order(order = 10)
	public void testProvider$Annotation()
	{
		System.out.println("=====================10");
		ProviderService$Annotation service = Guice.createInjector().getInstance(ProviderService$Annotation.class);
		service.service2.execute();
	}

	/**
	 * provider:注入Provider
	 * 
	 * juice.DI$multiInterface.service.Service<br>
	 * juice.DI$provider.module.HomeServiceProvider$Module<br>
	 */
	@Test
	@Order(order = 11)
	public void testProvider$Provider()
	{
		System.out.println("=====================11");
		Injector injector = Guice.createInjector(new Module()
		{
			@Override
			public void configure(Binder binder)
			{
				binder.bind(Service.class).toProvider(HomeServiceProvider$Module.class);
			}
		});

		ProviderInject inject = injector.getInstance(ProviderInject.class);
		inject.provider.get().execute();
	}

	/**
	 * 常量注入
	 */
	@Test
	@Order(order = 12)
	public void testConstant()
	{
		System.out.println("=====================12");
		ConstantInject inject = Guice.createInjector(new Module()
		{
			@Override
			public void configure(Binder binder)
			{
				// binder.bind(int.class).annotatedWith(Names.named("v")).toInstance(12);
				binder.bindConstant().annotatedWith(Names.named("v")).to(12);
				binder.bindConstant().annotatedWith(Names.named("s")).to("hello s");
				binder.bindConstant().annotatedWith(Names.named("w")).to("hello w");
			}
		}).getInstance(ConstantInject.class);
		System.out.println(inject.v);
		System.out.println(inject.s);
		System.out.println(inject.w);
	}

	/**
	 * 常量注入:使用Properties或者Map<String,String>
	 * 
	 * 测试成功：属性必须都是字符串类型
	 * 
	 * com.google.inject.binder.ConstantBindingBuilder.to(long)没有明白
	 */
	// @Test
	@Order(order = 13)
	public void testConstant$Properties()
	{
		System.out.println("=====================13");
		ConstantInject inject2 = Guice.createInjector(new Module()
		{

			@Override
			public void configure(Binder binder)
			{
				Properties pro = new Properties();
				pro.setProperty("s", "it's a nice s.");
				pro.setProperty("w", "it's a nice w.");
				Names.bindProperties(binder, pro);

				// Map<String, String> map = Maps.newHashMap();
				// map.put("s", "wa s");
				// map.put("w", "hao w");
				// Names.bindProperties(binder, map);
			}

		}).getInstance(ConstantInject.class);
		System.out.println(inject2.w);
	}

	/**
	 * Scope
	 */
	@Test
	@Order(order = 14)
	public void testScope()
	{
		System.out.println("=====================14");
		final Injector injector = Guice.createInjector(new Module()
		{

			@Override
			public void configure(Binder binder)
			{
				binder.bind(Service.class).to(juice.DI$multiInterface.service.impl.HomeService.class).in(new ThreadServiceScope());
			}

		});

		for (int i = 0; i < 3; i++)
		{
			new Thread("thread-" + i)
			{
				@Override
				public void run()
				{
					for (int j = 0; j < 3; j++)
					{
						System.out.println(String.format("%s-%d:%d", getName(), j, injector.getInstance(Service.class).hashCode()));
					}

					try
					{
						sleep(500L);
					}
					catch (InterruptedException e)
					{
						e.printStackTrace();
					}
				};
			}.start();
		}
	}

	/**
	 * 
	 */
	@Test
	@Order(order = 1)
	public void test()
	{
		System.out.println("=====================");
	}
}
